package com.example.config;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.json.JSONUtil;
import com.example.common.HttpStatusEnum;
import com.example.entity.ResponseData;
import com.example.mapper.ChatMapper;
import com.example.pojo.Chat;
import com.example.util.CommonUtil;
import com.example.util.SpringGetBeanUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArraySet;

/** webscoket服务
 *
 * @email: pengyujun53@163.com
 * @author: peng_YuJun
 * @date: 2023/3/14
 * @time: 23:24
 */

@Slf4j
@Component
@ServerEndpoint("/wsChat/{fromId}/{toId}")
public class WebSocket {
    //实例一个session，这个session是websocket的session
    private Session session;

    //存放websocket的集合
    private static CopyOnWriteArraySet<WebSocket> webSocketSet = new CopyOnWriteArraySet<>();
    private static Map<Long,WebSocket> webSocketMap = new HashMap<>();

    //信息的发送者id和接收者id
    private Long fromId;
    private Long toId;

    //总感觉在这个高频度调用的场景下，频繁新建chatService不太好。
    //不过@Component由于和其他容器注解创建顺序的关系，没办法单例注入，先用着看看吧
    private ChatMapper chatMapper;

    //前端请求时一个websocket时
    @OnOpen
    public void onOpen(Session session,@PathParam("fromId") Long fromId,@PathParam("toId") Long toId) {
        this.session = session;
        this.fromId = fromId;
        this.toId = toId;
        webSocketMap.put(fromId,this);
        webSocketSet.add(this);
        //在这里初始化，避免多次调用
        chatMapper = SpringGetBeanUtil.getBean(ChatMapper.class);
        //log.info("【websocket消息】有新的连接, 总数:{}", webSocketSet.size());
    }

    //前端关闭时一个websocket时
    @OnClose
    public void onClose() {
        webSocketMap.remove(fromId);
        webSocketSet.remove(this);
        //log.info("【websocket消息】连接断开, 总数:{}", webSocketSet.size());
    }

    /**
     * 配置错误信息处理
     * @param t
     */
    @OnError
    public void onError(Throwable t) {
        //什么都不想打印都去掉就好了
        log.info("【websocket消息】出现未知错误",t);
    }

    //前端向后端发送消息
    @OnMessage
    public void onMessage(String message) {
        //log.info("【websocket消息】收到客户端发来的消息:{}", message);
        ResponseData data = new ResponseData();
        //心跳包机制
        if (message.equals("HeartBeatFromMultipleApp")){
            data.setCode(HttpStatusEnum.HEART_BEAT_OK.getCode()).setMsg("HeartBeat成功！");
            sendSomeOneMsg(JSONUtil.toJsonStr(data));
            return;
        }
        //接发信息程序
        Chat chat = JSONUtil.toBean(message, Chat.class);
        chat.setFromId(fromId);
        chat.setToId(toId);
        chat.setTime(new Date());
        chat.setSeen(0);
        chat.setDeleted(0);
        if (chatMapper.insert(chat) > 0){ //保存成功
            //为了即时显示时间到页面，做了响应调整
            Map<String, Object> respData = BeanUtil.beanToMap(chat);
            respData.put("time", CommonUtil.DateToString(new Date()));
            data.setData(respData).setCode(HttpStatusEnum.SUCCESS.getCode()).setMsg(HttpStatusEnum.SUCCESS.getMessage());
            if (toId == 0){
                //群聊
                for (WebSocket item : webSocketSet){
                    if (item.toId == 0){
                        //确认接收消息的人同样在群聊
                        item.sendSomeOneMsg(JSONUtil.toJsonStr(data));
                    }
                }
            }else {
                //非群聊
                WebSocket webSocket = webSocketMap.get(toId);
                //确认接收消息的人在和自己聊天
                if (webSocket!= null && webSocket.toId == fromId){
                    webSocket.sendSomeOneMsg(JSONUtil.toJsonStr(data));
                }
                //给自己发一份
                sendSomeOneMsg(JSONUtil.toJsonStr(data));
            }
        }else {
            data.setCode(HttpStatusEnum.ERROR.getCode()).setMsg("发送失败，请稍后再试");
            sendSomeOneMsg(JSONUtil.toJsonStr(data));
        }
    }

    //新增一个方法用于主动向客户端发送消息
    public void sendSomeOneMsg(String message) {
        //log.info("【websocket消息】发送消息, message={}", message);
        try {
            this.session.getBasicRemote().sendText(message);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
